// DemoDlg.cpp : 实现文件
//Implementation file

#include "stdafx.h"
#include "SingleDisplay.h"
#include "SingleDisplayDlg.h"
#include "RGBConvert.h"
#include "SingleDisplayDlg.h"
#include "ConfigOperate.h"
#include <sstream>
#include <io.h>
#include <fcntl.h>
#include <afx.h>
extern ::CString getParseLanguageString(::CString strOrl);

#import "msxml3.dll"
using namespace MSXML2;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define DEFAULT_SHOW_RATE (30)

using namespace Dahua;
using namespace Dahua::Infra;


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// 对话框数据
// dialog data
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CSingleDisplayDlg 对话框
// CSingleDisplayDlg dialog
CSingleDisplayDlg::CSingleDisplayDlg(CWnd* pParent)
	: CDialog(CSingleDisplayDlg::IDD, pParent)
	, _connected(false)
	, _isGrabbing(false)
	, _bRunning(false)
	, _frameCnt(0)
	, _frameCtrl(false)
	, _maxFrameCnt(0)
	, _devListDlg(NULL)
	, _displayThread(CThreadLite::ThreadProc(&CSingleDisplayDlg::DisplayThreadProc, this), "Display")
//	, _streamRetrieveThread(CThreadLite::ThreadProc(&CSingleDisplayDlg::StreamRetrieveThreadPro, this), "StreamRetrieve")
	, m_dDisplayInterval(0)
	, m_nFirstFrameTime(0)
	, m_nLastFrameTime(0)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	setDisplayFPS(30);   // 默认显示30帧 | Default display 30 frames
}

void CSingleDisplayDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);

	_render.setHandle(GetDlgItem(IDC_DISPLAY)->GetSafeHwnd());
}

BOOL CSingleDisplayDlg::OpenConsole()
{
	AllocConsole();
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	int hCrt = _open_osfhandle(static_cast<long>(reinterpret_cast<uintptr_t>(handle)), _O_TEXT);
	FILE* hf = _fdopen(hCrt, "w");
	*stdout = *hf;
	return TRUE;
}

BEGIN_MESSAGE_MAP(CSingleDisplayDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_BTN_CONNECT, &CSingleDisplayDlg::OnBnClickedConnect)
	ON_BN_CLICKED(IDC_BTN_PLAY, &CSingleDisplayDlg::OnBnClickedBtnPlay)
	ON_WM_CLOSE()
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_FRAMERATE, &CSingleDisplayDlg::OnNMCustomdrawSliderFramerate)
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_EXPOSURETIME, &CSingleDisplayDlg::OnNMCustomdrawSliderExposuretime)
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_GAIN, &CSingleDisplayDlg::OnNMCustomdrawSliderGain)
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_GAMA, &CSingleDisplayDlg::OnNMCustomdrawSliderGama)
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_LIGHT, &CSingleDisplayDlg::OnNMCustomdrawSliderLight)
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_SHARPNESS, &CSingleDisplayDlg::OnNMCustomdrawSliderSharpness)
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_HUE, &CSingleDisplayDlg::OnNMCustomdrawSliderHue)
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_SATURATION, &CSingleDisplayDlg::OnNMCustomdrawSliderSaturation)
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_RWHITEBALANCE, &CSingleDisplayDlg::OnNMCustomdrawSliderRwhitebalance)
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_GWHITEBALANCE, &CSingleDisplayDlg::OnNMCustomdrawSliderGwhitebalance)
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_BWHITEBALANCE, &CSingleDisplayDlg::OnNMCustomdrawSliderBwhitebalance)

	ON_CBN_SELCHANGE(IDC_COMBO_FORMAT, &CSingleDisplayDlg::OnCbnSelchangeComboFormat)
	ON_CBN_SELCHANGE(IDC_COMBO_AUTOEXPOSURE, &CSingleDisplayDlg::OnCbnSelchangeComboAutoexposure)
	ON_CBN_SELCHANGE(IDC_COMBO_EXPOSUREMODE, &CSingleDisplayDlg::OnCbnSelchangeComboExposuremode)
	ON_CBN_SELCHANGE(IDC_COMBO_AUTOGAIN, &CSingleDisplayDlg::OnCbnSelchangeComboAutogain)
	ON_CBN_SELCHANGE(IDC_COMBO_AUTOWHITEBALANCE, &CSingleDisplayDlg::OnCbnSelchangeComboAutowhitebalance)
	ON_CBN_SELCHANGE(IDC_COMBO_PLAYMODE, &CSingleDisplayDlg::OnCbnSelchangeComboPlaymode)
	ON_BN_CLICKED(IDC_BTN_HORITRANS, &CSingleDisplayDlg::OnBnClickedBtnHoritrans)
	ON_BN_CLICKED(IDC_BTN_VERTTRANS, &CSingleDisplayDlg::OnBnClickedBtnVerttrans)
	ON_BN_CLICKED(IDC_BTN_SOFTTRIGGER, &CSingleDisplayDlg::OnBnClickedBtnSofttrigger)
	ON_CBN_SELCHANGE(IDC_COMBO_TRIGGERMODE, &CSingleDisplayDlg::OnCbnSelchangeComboTriggermode)
	ON_BN_CLICKED(IDC_BTN_SAVECFG, &CSingleDisplayDlg::OnBnClickedBtnSavecfg)
	ON_BN_CLICKED(IDC_BTN_OPENCFG, &CSingleDisplayDlg::OnBnClickedBtnOpencfg)

	ON_BN_CLICKED(IDC_BTN_SAVEPIC, &CSingleDisplayDlg::OnBnClickedBtnSavepic)
END_MESSAGE_MAP()

bool printCallBack(char const* p)
{
	std::cout << p << std::endl;
	return true;
}

// CSingleDisplayDlg 消息处理程序
// CSingleDisplayDlg message processing program

BOOL CSingleDisplayDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。
	// put about ..add menu item to system menu
	// IDM_ABOUTBOX 必须在系统命令范围内。 | IDM_ABOUTBOX must be within system command.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		::CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 根据目前配置的语言初始化界面文本显示 | Initialize the text display of the interface according to the currently configured language
	{
		this->SetWindowText(getParseLanguageString(_T("_MAINFORM_TEXT")));
		GetDlgItem(IDC_BTN_CONNECT)->SetWindowText(getParseLanguageString(_T("_MAINFORM_CONNECT")));
		GetDlgItem(IDC_BTN_PLAY)->SetWindowText(getParseLanguageString(_T("_MAINFORM_PLAY")));
		GetDlgItem(IDC_BTN_HORITRANS)->SetWindowText(getParseLanguageString(_T("_MAINFORM_HORIZONFLIP")));
		GetDlgItem(IDC_BTN_VERTTRANS)->SetWindowText(getParseLanguageString(_T("_MAINFORM_VERTICALFLIP")));
		GetDlgItem(IDC_BTN_SOFTTRIGGER)->SetWindowText(getParseLanguageString(_T("_MAINFORM_SOFTTRIGGER")));
		GetDlgItem(IDC_FRAME_COUNT)->SetWindowText(getParseLanguageString(_T("_MAINFORM_FRAMECOUNT")));
		GetDlgItem(IDC_BTN_SAVEPIC)->SetWindowText(getParseLanguageString(_T("_MAINFORM_GETIMAGE")));
		GetDlgItem(IDC_BTN_SAVECFG)->SetWindowText(getParseLanguageString(_T("_MAINFORM_SAVECONFIG")));
		GetDlgItem(IDC_BTN_OPENCFG)->SetWindowText(getParseLanguageString(_T("_MAINFORM_OPENCONFIG")));
		GetDlgItem(IDC_STATIC_SETCONFIG)->SetWindowText(getParseLanguageString(_T("_CFGFORM_CONFIGSET")));
		GetDlgItem(IDC_FRAME_RATE)->SetWindowText(getParseLanguageString(_T("_CFGFORM_FRAMERATE")));
		GetDlgItem(IDC_IMAGE_FORMAT)->SetWindowText(getParseLanguageString(_T("_CFGFORM_IMAGEFORMAT")));
		GetDlgItem(IDC_EXPOSURE)->SetWindowText(getParseLanguageString(_T("_CFGFORM_EXPOSURE")));
		GetDlgItem(IDC_EXPOSE_VALUE)->SetWindowText(getParseLanguageString(_T("_CFGFORM_EXPOSETIMEVALUE")));
		GetDlgItem(IDC_AUTO_EXPOSURE)->SetWindowText(getParseLanguageString(_T("_CFGFORM_AUTOEXPOSE")));
		GetDlgItem(IDC_EXPOSE_MODE)->SetWindowText(getParseLanguageString(_T("_CFGFORM_MODE")));
		GetDlgItem(IDC_GAIN)->SetWindowText(getParseLanguageString(_T("_CFGFORM_GAIN")));
		GetDlgItem(IDC_GAIN_VALUE)->SetWindowText(getParseLanguageString(_T("_CFGFORM_GAINVALUE")));
		GetDlgItem(IDC_AUTO_GAIN)->SetWindowText(getParseLanguageString(_T("_CFGFORM_AUTOGAIN")));
		GetDlgItem(IDC_GAMA)->SetWindowText(getParseLanguageString(_T("_CFGFORM_GAMMA")));
		GetDlgItem(IDC_LIGHT)->SetWindowText(getParseLanguageString(_T("_CFGFORM_LIGHT")));
		GetDlgItem(IDC_SHARPNESS)->SetWindowText(getParseLanguageString(_T("_CFGFORM_SHAPNESS")));
		GetDlgItem(IDC_HUE)->SetWindowText(getParseLanguageString(_T("_CFGFORM_HUE")));
		GetDlgItem(IDC_SATURATION)->SetWindowText(getParseLanguageString(_T("_CFGFORM_SATURATION")));
		GetDlgItem(IDC_WHITE_BALANCE)->SetWindowText(getParseLanguageString(_T("_CFGFORM_WHITEBALANCE")));
		GetDlgItem(IDC_WHITEBALANCE_RGAIN)->SetWindowText(getParseLanguageString(_T("_CFGFORM_WHIBALRGAIN")));
		GetDlgItem(IDC_WHITEBALANCE_GGAIN)->SetWindowText(getParseLanguageString(_T("_CFGFORM_WHIBALGGAIN")));
		GetDlgItem(IDC_WHITEBALANCE_B_GAIN)->SetWindowText(getParseLanguageString(_T("_CFGFORM_WHIBALBGAIN")));
		GetDlgItem(IDC_AUTO_WHITEBALANCE)->SetWindowText(getParseLanguageString(_T("_CFGFORM_AUTOWHIBAL")));

	}

	// 设置此对话框的图标。当应用程序主窗口不是对话框时，框架将自动
	// 执行此操作
	// set the icon for this dialog box. When the application's main window is not a dialog box, the framework does this automatically
	SetIcon(m_hIcon, TRUE);			// 设置大图标 |set big icon 
	SetIcon(m_hIcon, FALSE);		// 设置小图标 |set small icon 
	

	//Dahua::Infra::attachPrint(Dahua::Infra::PrintProc(&printCallBack));
	//Dahua::Infra::setPrintLogLevel(1);

	// TODO: 在此添加额外的初始化代码
	// add additional initialization code here
	EnableBtns(false);
	
	// 启动显示线程
	// start display thread
	if (!_displayThread.isThreadOver())
	{
		_displayThread.destroyThread();
	}
	
	if (!_displayThread.createThread())
	{
		MessageBox("Create Display Thread Failed.", "", 0);
		return FALSE;
	}
	
	// 初始化属性控件
	// initialize property control
	InitAttributes();
	
	return TRUE;  // 除非将焦点设置到控件，否则返回 TRUE | return true unless focus is set to control
}


// 使能按钮
// enable button
void CSingleDisplayDlg::EnableBtns(bool enable)
{
	GetDlgItem(IDC_BTN_PLAY)->EnableWindow(enable);
	GetDlgItem(IDC_BTN_SOFTTRIGGER)->EnableWindow(enable);
	GetDlgItem(IDC_BTN_HORITRANS)->EnableWindow(enable);
	GetDlgItem(IDC_BTN_VERTTRANS)->EnableWindow(enable);
	GetDlgItem(IDC_BTN_SAVEPIC)->EnableWindow(enable);
}

void CSingleDisplayDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// 显示线程
// display thread
void CSingleDisplayDlg::DisplayThreadProc(Dahua::Infra::CThreadLite& lite)
{
	while ( lite.looping() )
	{
		if(!_bRunning)
		{
			Sleep(10);
			continue;
		}

		if(m_bStopGrabbing)
		{
			StopStreamGrabbing();
		}

		FrameBufferSPtr	displayFrame = getConvertedImage();

		if (!displayFrame )
		{
			continue;
		}

		_render.display(displayFrame->bufPtr(), displayFrame->Width(), displayFrame->Height(), displayFrame->PixelFormat());
	}
}

bool CSingleDisplayDlg::isTimeToDisplay()
{
	Infra::CGuard guard(m_mxTime);

	// 不显示
	// not display
	if (m_dDisplayInterval <= 0)
	{
		return false;
	}

	// 第一帧必须显示
	// the first frame must be displayed
	if (m_nFirstFrameTime == 0 || m_nLastFrameTime == 0)
	{
		m_nFirstFrameTime = Infra::CTime::getCurrentMicroSecond() * 1000;;
		m_nLastFrameTime = m_nFirstFrameTime;

		TRACE("set m_nFirstFrameTime: %I64d\n", m_nFirstFrameTime);
		return true;
	}

	uint64_t nCurTimeTmp = Infra::CTime::getCurrentMicroSecond() * 1000;
	uint64_t nAcquisitionInterval = nCurTimeTmp - m_nLastFrameTime;
	if (nAcquisitionInterval > m_dDisplayInterval)
	{
		m_nLastFrameTime = nCurTimeTmp;
		return true;
	}

	// 当前帧相对于第一帧的时间间隔
	// time interval between the current frame and the first frame
	uint64_t nPre = (m_nLastFrameTime - m_nFirstFrameTime) % m_dDisplayInterval;
	if (nPre + nAcquisitionInterval > m_dDisplayInterval)
	{
		m_nLastFrameTime = nCurTimeTmp;
		return true;
	}

	return false;
}

void CSingleDisplayDlg::setDisplayFPS(int nFPS)
{
	if (nFPS > 0)
	{
		Infra::CGuard guard(m_mxTime);
		m_dDisplayInterval = 1000 * 1000 * 1000.0 / nFPS;
	}
	else
	{
		Infra::CGuard guard(m_mxTime);
		m_dDisplayInterval = 0;
	}
}

// 用于拉流时判断参数是否可写，若不可写置灰控件
// it is used to judge whether the parameter is writable when start grabbing. If it is not writable, grey control will be set
void CSingleDisplayDlg::UpdateAttrStatus()
{
	AttrCtrlMap::iterator it = _attrCtrlMap.begin();
	for (; it != _attrCtrlMap.end(); ++it)
	{
		if (it->second)
		{
			it->second->updataStatus(_currentCamera);
		}
	}
}

// 用于Edit控件的回车响应
// using for return response for edit control
void CSingleDisplayDlg::OnEditCtrlModify(CWnd* wnd)
{
	AttrCtrlMap::iterator it = _attrCtrlMap.begin();
	for (; it != _attrCtrlMap.end(); ++it)
	{
		if (it->second && it->second->getWnd() == wnd)
		{
			it->second->updataValue(_currentCamera, EDIT_CHANGE);
		}

		// 设置界面多帧
		// set interface multi frame
		if(GetDlgItem(IDC_EDIT_FRAMENBR) == wnd)
		{
			::CString text;
			int64_t val;
			GetDlgItem(IDC_EDIT_FRAMENBR)->GetWindowText(text);
			std::stringstream ss;
			ss << text;
			ss >> val;

			_maxFrameCnt = (int)val;
		}
	}
}

// 拦截ESC以及回车键
// intercept ESC and enter
BOOL CSingleDisplayDlg::PreTranslateMessage(MSG* pMsg)
{
	if (pMsg->message == WM_KEYDOWN)
	{
		switch (pMsg->wParam)
		{
		case VK_ESCAPE:
			return TRUE;
		case VK_RETURN:
			// 通过焦点获取控件  |get control through focus
			OnEditCtrlModify(GetFocus());
			return TRUE;
		case VK_F4:
			OpenConsole();
			return TRUE;
		}
	}
	return CDialog::PreTranslateMessage(pMsg);
}


// 如果向对话框添加最小化按钮，则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序，
// 这将由框架自动完成。
// If you add a minimize button to the dialog box, you need the following code to draw the icon. 
// For MFC applications that use the document / view model, this is done automatically by the framework.
void CSingleDisplayDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文 | device context for drawing
		
		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
		
		// 使图标在工作矩形中居中
		// center icon in working rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;
		
		// 绘制图标
		// drawing icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}


// 当用户拖动最小化窗口时系统调用此函数取得光标显示。
// When the user drags the minimized window, the system calls this function to get the cursor display.
HCURSOR CSingleDisplayDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

// 初始化配置相关的属性
// initialize configuration related properties
void CSingleDisplayDlg::LoadCfg()
{
	ASSERT(NULL != _currentCamera);

	AttrCtrlMap::iterator it = _attrCtrlMap.begin();
	for (; it != _attrCtrlMap.end(); ++it)
	{
		if (it->second )
		{
			it->second->init(_currentCamera);
			it->second->updataStatus(_currentCamera);
		}
	}
}

void CSingleDisplayDlg::OnConnectEvent(const Dahua::GenICam::SConnectArg& arg)
{
	if (arg.m_event == Dahua::GenICam::SConnectArg::offLine)
	{
		StopStreamGrabbing();
		EnableBtns(false);
		_connected = false;
		SetDlgItemText(IDC_BTN_CONNECT, "Connect");
		MessageBox(_T("相机断线"), _T(""), 0);


		Dahua::GenICam::IEventSubscribePtr evtSubscribePtr = 
			Dahua::GenICam::CSystem::getInstance().createEventSubscribe(_currentCamera);
		assert(evtSubscribePtr);

		if (!evtSubscribePtr->unsubscribeConnectArgs(Dahua::GenICam::ConnectArgProc(&CSingleDisplayDlg::OnConnectEvent, this)))
		{
			;
		}
	}
}

void CSingleDisplayDlg::onGetFrame(const Dahua::GenICam::CFrame & frame)
{
	// 帧统计，如果AcquisitionMode属性非Continuous则需要统计帧数，以判断播放是否结束
	// Frame statistics. If acquisitionmode attribute is not continuous, you need to count the number of frames to determine whether the playback is over
	_frameCnt++;
	if (_frameCtrl && _frameCnt >= _maxFrameCnt)
	{
		m_bStopGrabbing = true;
	}

	// 判断是否要显示。超过显示上限（30帧），就不做转码、显示处理
	// determine whether to display. If the upper display limit (30 frames) is exceeded, transcoding and display processing will not be performed
	if (!this->isTimeToDisplay())
	{
		return;
	}

	// 转码
	// transcoding
	FrameBufferSPtr sptrConvertFrameBuffer;
	if(ConvertToBGR24(frame, sptrConvertFrameBuffer))
	{
		addConvertedImage(sptrConvertFrameBuffer);
	}
}

// DeviceList Dialog中连接结果的回调函数
// callback function of connection result in devicelist dialog
void CSingleDisplayDlg::OnConnect(Dahua::GenICam::ICameraPtr camera, bool ret)
{
	if (!ret)
	{
		return;
	}
	
	// 使能按钮
	// enable button
	EnableBtns(true);
	
	_currentCamera = camera;
	_connected = true;

	_stream = GenICam::CSystem::getInstance().createStreamSource(_currentCamera);
	if (NULL == _stream)
	{
		MessageBox("Get Stream Failed.", "", 0);
	}

	Dahua::GenICam::IEventSubscribePtr evtSubscribePtr = 
				Dahua::GenICam::CSystem::getInstance().createEventSubscribe(_currentCamera);
	assert(evtSubscribePtr);
	
	if (!evtSubscribePtr->subscribeConnectArgs(Dahua::GenICam::ConnectArgProc(&CSingleDisplayDlg::OnConnectEvent, this)))
	{
		MessageBox(_T("添加断线回调失败"), _T(""), 0);
	}
	
	LoadCfg();
	
	SetDlgItemText(IDC_BTN_CONNECT, "Disconnect");
}

// 连接按钮被按下
// connection button pressed
void CSingleDisplayDlg::OnBnClickedConnect()
{
	CButton* pConnectBtn = (CButton*)GetDlgItem(IDC_BTN_CONNECT);
	ASSERT(pConnectBtn != NULL);
	
	if (!_connected)
	{
		if (NULL != _devListDlg)
		{
			delete _devListDlg;
		}
		_devListDlg = new CDeviceList;
		_devListDlg->Create(IDD_DEVICELIST, this);
		_devListDlg->initDlg(CDeviceList::Proc(&CSingleDisplayDlg::OnConnect, this));
		
		// 将设备列表窗口移到合适位置
		// move the device list window to the appropriate location
		CRect rect, rectSelf;
		_devListDlg->GetClientRect(&rect);
		GetClientRect(&rectSelf);
		this->ClientToScreen(&rectSelf);
		int x = rectSelf.TopLeft().x + rectSelf.TopLeft().x / 2;
		int y = rectSelf.TopLeft().y + rectSelf.TopLeft().y / 2;
		int width = rect.Width();
		int height = rect.Height();
		_devListDlg->MoveWindow(x, y, width, height);
		_devListDlg->ShowWindow(SW_SHOW);
	}
	else
	{
		StopStreamGrabbing();
		
		if (NULL != _currentCamera && !_currentCamera->disConnect())
		{
			MessageBox("Disconnect Failed.", "", 0);
		}
		EnableBtns(false);
		_connected = false;
		SetDlgItemText(IDC_BTN_CONNECT, _T("Connecting"));

		Dahua::GenICam::IEventSubscribePtr evtSubscribePtr = 
			Dahua::GenICam::CSystem::getInstance().createEventSubscribe(_currentCamera);
		assert(evtSubscribePtr);

		if (!evtSubscribePtr->unsubscribeConnectArgs(Dahua::GenICam::ConnectArgProc(&CSingleDisplayDlg::OnConnectEvent, this)))
		{
			MessageBox(_T(" 删除断线回调失败"), _T(""), 0);
		}
	}
}

// 播放按钮响应函数
// play button response function
void CSingleDisplayDlg::OnBnClickedBtnPlay()
{
	if (NULL == _currentCamera)
	{
		return;
	}
	
	if (_stream != NULL && _isGrabbing == true)
	{
		StopStreamGrabbing();
	}
	else
	{
		StartStreamGrabbing();
	}
}

// 窗口关闭响应函数
// window close response function
void CSingleDisplayDlg::OnClose()
{
	if (!_displayThread.isThreadOver())
	{
		_displayThread.destroyThread();
	}

// 	关闭取流线程 | close get frame thread
// 	if(!_streamRetrieveThread.isThreadOver())
// 	{
// 		_streamRetrieveThread.destroyThread();
// 	}

	if (_devListDlg != NULL)
	{
		delete _devListDlg;
	}
	
	StopStreamGrabbing();
	
	if (_currentCamera != NULL)
	{
		_currentCamera->disConnect();
		_currentCamera.reset();
	}
	
	CDialog::OnClose();
}

// 开始拉流
// start grabbing
bool CSingleDisplayDlg::StartStreamGrabbing()
{

	_stream->attachGrabbing(GenICam::IStreamSource::Proc(&CSingleDisplayDlg::onGetFrame,this));

	m_bStopGrabbing = false;

	// 显示打开
	// open display
	if (!_render.isOpen())
	{
		_render.open();
		if (!_render.isOpen())
		{
			return false;
		}
	}

	_frameCnt = 0;
	if (!_stream->startGrabbing())
	{
		MessageBox("Start Stream Grabbing Failed.", "", 0);
		return false;
	}

	UpdateAttrStatus();
	SetDlgItemText(IDC_BTN_PLAY, _T("Stop"));

	_bRunning = true;
	_isGrabbing = true;

	return true;
}

// 停止拉流
// stop grabbing
bool CSingleDisplayDlg::StopStreamGrabbing()
{
	
	_bRunning = false;

	m_bStopGrabbing = true;

	if (_stream == NULL)
		return true;
	
	_isGrabbing = false;


	_stream->detachGrabbing(GenICam::IStreamSource::Proc(&CSingleDisplayDlg::onGetFrame,this));	


	if(!_stream->stopGrabbing())
	{
		return false;
	}

	{
	 	Infra::CGuard guard(m_mutexQue);
	 	_displayQue.clear();
	}
	
	UpdateAttrStatus();
	SetDlgItemText(IDC_BTN_PLAY, _T("Play"));
	
	return true;
}

// 初始化控件
// initialize control button
void CSingleDisplayDlg::InitAttributes()
{
	_attrCtrlMap[SATURATION] = AttrCtrlBasePtr( new SaturationAttr(this) );
	_attrCtrlMap[FRAMERATE] = AttrCtrlBasePtr( new FrameRateAttr(this) );
	_attrCtrlMap[EXPOSURETIME] = AttrCtrlBasePtr( new ExposureTimeAttr(this) );
	_attrCtrlMap[GAINRAW] = AttrCtrlBasePtr( new GainRawAttr(this) );
	_attrCtrlMap[GAMMA] = AttrCtrlBasePtr( new GammaAttr(this) );
	_attrCtrlMap[BRIGHTNESS] = AttrCtrlBasePtr( new BrightnessAttr(this) );
	_attrCtrlMap[SHARPNESS] = AttrCtrlBasePtr( new SharpnessAttr(this) );
	_attrCtrlMap[HUE] = AttrCtrlBasePtr( new HueAttr(this) );
	_attrCtrlMap[WHITEBALANCE_R] = AttrCtrlBasePtr( new WhiteBalanceR(this) );
	_attrCtrlMap[WHITEBALANCE_G] = AttrCtrlBasePtr( new WhiteBalanceG(this) );
	_attrCtrlMap[WHITEBALANCE_B] = AttrCtrlBasePtr( new WhiteBalanceB(this) );
	_attrCtrlMap[PICFORMAT] = AttrCtrlBasePtr( new CPictureFormatAttr(this) );
	_attrCtrlMap[AUTOEXPOSURE] = AttrCtrlBasePtr( new CAutoExposureAttr(this) );
	_attrCtrlMap[EXPOSUREMODE] = AttrCtrlBasePtr( new CExposureModeAttr(this) );
	_attrCtrlMap[GAINAUTO] = AttrCtrlBasePtr( new CAutoGainAttr(this) );
	_attrCtrlMap[BLACKLEVEL] = AttrCtrlBasePtr( new CWhitBalanceAttr(this) );
	_attrCtrlMap[PLAYMODE] = AttrCtrlBasePtr( new CAcquisitionModeAttr(this, _frameCtrl, _maxFrameCnt, _frameCnt) );
	_attrCtrlMap[REVERSEX] = AttrCtrlBasePtr( new CReverseX(this) );
	_attrCtrlMap[REVERSEY] = AttrCtrlBasePtr( new CReverseY(this) );
	_attrCtrlMap[TRIGGERMODE] = AttrCtrlBasePtr( new CTriggerModeAttr(this) );
	_attrCtrlMap[SOFTTRIGGER] = AttrCtrlBasePtr( new CSoftTrigger(this) );
	_attrCtrlMap[MUTIFRAMECNT] = AttrCtrlBasePtr( new CMutiFrameEdit(this) );
}

void CSingleDisplayDlg::OnNMCustomdrawSliderFramerate(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	*pResult = 0;
	
	_attrCtrlMap[FRAMERATE]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnNMCustomdrawSliderExposuretime(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	*pResult = 0;
	
	_attrCtrlMap[EXPOSURETIME]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnNMCustomdrawSliderGain(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	*pResult = 0;
	
	_attrCtrlMap[GAINRAW]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnNMCustomdrawSliderGama(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	*pResult = 0;

	_attrCtrlMap[GAMMA]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnNMCustomdrawSliderLight(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	*pResult = 0;

	_attrCtrlMap[BRIGHTNESS]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnNMCustomdrawSliderSharpness(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	*pResult = 0;
	
	_attrCtrlMap[SHARPNESS]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnNMCustomdrawSliderHue(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	*pResult = 0;

	_attrCtrlMap[HUE]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnNMCustomdrawSliderSaturation(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	*pResult = 0;
	
	_attrCtrlMap[SATURATION]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnNMCustomdrawSliderRwhitebalance(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	*pResult = 0;
	
	_attrCtrlMap[WHITEBALANCE_R]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnNMCustomdrawSliderGwhitebalance(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	*pResult = 0;

	_attrCtrlMap[WHITEBALANCE_G]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnNMCustomdrawSliderBwhitebalance(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	*pResult = 0;

	_attrCtrlMap[WHITEBALANCE_B]->updataValue(_currentCamera, SLIDER_CHANGE);
}

void CSingleDisplayDlg::OnCbnSelchangeComboFormat()
{
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	
	_attrCtrlMap[PICFORMAT]->updataValue(_currentCamera, 0);
}

void CSingleDisplayDlg::OnCbnSelchangeComboAutoexposure()
{
	// TODO: 在此添加控件通知处理程序代码 | add control notification processing code here
	
	_attrCtrlMap[AUTOEXPOSURE]->updataValue(_currentCamera, 0);
}

void CSingleDisplayDlg::OnCbnSelchangeComboExposuremode()
{
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here

	_attrCtrlMap[EXPOSUREMODE]->updataValue(_currentCamera, 0);
}

void CSingleDisplayDlg::OnCbnSelchangeComboAutogain()
{
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here

	_attrCtrlMap[GAINAUTO]->updataValue(_currentCamera, 0);
}

void CSingleDisplayDlg::OnCbnSelchangeComboAutowhitebalance()
{
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here

	_attrCtrlMap[BLACKLEVEL]->updataValue(_currentCamera, 0);
}

void CSingleDisplayDlg::OnCbnSelchangeComboPlaymode()
{
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here

	_attrCtrlMap[PLAYMODE]->updataValue(_currentCamera, 0);
}

void CSingleDisplayDlg::OnBnClickedBtnHoritrans()
{
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here

	_attrCtrlMap[REVERSEX]->updataValue(_currentCamera, 0);
}

void CSingleDisplayDlg::OnBnClickedBtnVerttrans()
{
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	
	_attrCtrlMap[REVERSEY]->updataValue(_currentCamera, 0);
}

void CSingleDisplayDlg::OnBnClickedBtnSofttrigger()
{
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here
	
	_attrCtrlMap[SOFTTRIGGER]->updataValue(_currentCamera, 0);
}

void CSingleDisplayDlg::OnCbnSelchangeComboTriggermode()
{
	// TODO: 在此添加控件通知处理程序代码 |add control notification processing code here

	_attrCtrlMap[TRIGGERMODE]->updataValue(_currentCamera, 0);
}

::CString CSingleDisplayDlg::GetSelectFile()
{
	::CString strFile = "";
	CFileDialog dlgFile(TRUE, NULL, NULL, OFN_HIDEREADONLY, "Describe Files(*.xml)|*.xml|All Files (*.*)|*.*||", NULL);
	if (dlgFile.DoModal())
	{
		strFile = dlgFile.GetPathName();
	}
	return strFile;
}

// 保存配置 | save configuration
void CSingleDisplayDlg::GetCfgXml(::CString const& fileName, std::vector<XmlCfgItem>& vec)
{
	::CoInitialize(NULL); 

	MSXML2::IXMLDOMDocumentPtr XMLDOC;  
	MSXML2::IXMLDOMElementPtr XMLROOT; 
	MSXML2::IXMLDOMElementPtr XMLELEMENT; 
	MSXML2::IXMLDOMNodeListPtr XMLNODES; //某个节点的所有子节点 | all children of a node
	MSXML2::IXMLDOMNamedNodeMapPtr XMLNODEATTS;//某个节点的所有属性;| all properties of a node
	MSXML2::IXMLDOMNodePtr XMLNODE; 
	HRESULT HR = XMLDOC.CreateInstance(_uuidof(MSXML2::DOMDocument30)); 
	if(!SUCCEEDED(HR)) 
	{ 
		MessageBox("faild!!"); 
		return; 
	} 
	
	XMLDOC->load((_variant_t)fileName); 
	XMLROOT = XMLDOC->GetdocumentElement();//获得根节点;| get boot node
	XMLROOT->get_childNodes(&XMLNODES);//获得根节点的所有子节点;| get all children of the root node
	long XMLNODESNUM,ATTSNUM; 
	XMLNODES->get_length(&XMLNODESNUM);//获得所有子节点的个数; |  get the number of all child nodes

	for(int I = 0;I < XMLNODESNUM; I++) 
	{ 
		XMLNODES->get_item(I,&XMLNODE);//获得某个子节点; | get a child node
		XMLNODE->get_attributes(&XMLNODEATTS);//获得某个节点的所有属性; | get all attributes of a node;
		XMLNODEATTS->get_length(&ATTSNUM);//获得所有属性的个数; |get the number of all attributes;
		
		XmlCfgItem item;
		for(int J = 0;J < ATTSNUM; J++) 
		{ 
			XMLNODEATTS->get_item(J,&XMLNODE);//获得某个属性;| get a property
			::CString T1 = (char*)(_bstr_t)XMLNODE->nodeName; 
			::CString T2 = (char*)(_bstr_t)XMLNODE->text; 
			
			if (T1 == "name")
			{
				item.name = T2;
			}
			else if (T1 == "value")
			{
				item.value = T2;
			}
		} 
		vec.push_back(item);
	} 

	XMLNODES.Release(); 
	XMLNODE.Release(); 
	XMLROOT.Release(); 
	XMLDOC.Release(); 

	::CoUninitialize(); 
}

// 解析配置
// parse configuration
void CSingleDisplayDlg::SaveCfgXml(::CString const& fileName, std::vector<XmlCfgItem>& vec)
{
	::CoInitialize(NULL); 

	MSXML2::IXMLDOMDocumentPtr XMLDOC; 
	MSXML2::IXMLDOMElementPtr XMLROOT; 
	HRESULT HR = XMLDOC.CreateInstance(_uuidof(MSXML2::DOMDocument30)); 
	if(!SUCCEEDED(HR)) 
	{ 
		MessageBox("faild!!"); 
		return; 
	} 
	XMLROOT = XMLDOC->createElement("Property"); 
	XMLDOC->appendChild(XMLROOT);

	XMLROOT->put_text((_bstr_t)"\n");
	
	MSXML2::IXMLDOMNodePtr pTextNode = XMLDOC->createTextNode("\t\t");
	XMLROOT->appendChild(pTextNode);
	
	MSXML2::IXMLDOMElementPtr XMLNODE;
	std::vector<XmlCfgItem>::iterator it = vec.begin();
	for (; it != vec.end(); ++it)
	{
		XMLNODE = XMLDOC->createElement((_bstr_t)("Item")); 
		//XMLNODE->put_text((_bstr_t)"Item");//设置标签的文本内容;  |set the text content of the label
		XMLNODE->setAttribute("name",(_variant_t)(*it).name.c_str());//设置标签的属性及内容; |set label properties and contents
		XMLNODE->setAttribute("value",(_variant_t)(*it).value.c_str()); 
		XMLROOT->appendChild(XMLNODE); 
		
		MSXML2::IXMLDOMNodePtr pTextNode1;
		if (it != --vec.end())
			pTextNode1 = XMLDOC->createTextNode("\r\n\t\t");
		else
			pTextNode1 = XMLDOC->createTextNode("\r\n");
		XMLROOT->appendChild(pTextNode1);
	}

	XMLDOC->save((_variant_t)fileName); 
	XMLNODE.Release(); 
	XMLROOT.Release(); 
	XMLDOC.Release(); 

	::CoUninitialize();
}

void CSingleDisplayDlg::OnBnClickedBtnSavecfg()
{
	::CString strFile = GetSelectFile();
	
	if (strFile == "" || _currentCamera == NULL)
	{
		return;
	}
	
	// 将所有配置信息导入到cfgItems中
	// import all configuration information to cfgItems
	std::vector< XmlCfgItem > cfgItemVec;
	AttrCtrlMap::iterator it = _attrCtrlMap.begin();
	for (; it != _attrCtrlMap.end(); ++it)
	{
		if (it->second)
		{
			it->second->writeCfg(_currentCamera, cfgItemVec);
		}
	}
	
	// 保存cfgItems到文件strFile
	//save cfgItems to strFile
	SaveCfgXml(strFile, cfgItemVec);

	MessageBox("保存配置成功", "", 0);
}

void CSingleDisplayDlg::OnBnClickedBtnOpencfg()
{
	::CString strFile = GetSelectFile();
	
	if (strFile == "" || _currentCamera == NULL)
	{
		return;
	}
	
	std::vector< XmlCfgItem > cfgItemVec;
	GetCfgXml(strFile, cfgItemVec);
	
	AttrCtrlMap::iterator it = _attrCtrlMap.begin();
	for (; it != _attrCtrlMap.end(); ++it)
	{
		if (it->second)
		{
			it->second->readCfg(_currentCamera, cfgItemVec);
		}
	}
	
	MessageBox("打开配置成功", "", 0);
}

//void CSingleDisplayDlg::StreamRetrieveThreadPro(Infra::CThreadLite& lite)
//{
//	while(lite.looping())
//	{
//		if (_stream == NULL || _isGrabbing == false || _bRunning == false)
//		{
//			continue;
//		}
//
//		GenICam::CFrame frameBuf;
//		_stream->getFrame(frameBuf, 100);
//
//		if(!frameBuf.valid())
//		{
//			continue;
//		}
//
//		// 判断是否要显示。超过显示上限（30帧），就不做转码、显示处理
//		if (!this->isTimeToDisplay())
//		{
//			continue;
//		}
//
//		// 转码
//		FrameBufferSPtr sptrConvertFrameBuffer;
//		if(ConvertToBGR24(frameBuf, sptrConvertFrameBuffer))
//		{
//			addConvertedImage(sptrConvertFrameBuffer);
//		}
//
//		// 帧统计，如果AcquisitionMode属性非Continuous则需要统计帧数，以判断播放是否结束
//		_frameCnt++;
//		if (_frameCtrl && _frameCnt >= _maxFrameCnt)
//		{
//			StopStreamGrabbing();
//		}
//	}
//	
//}

FrameBufferSPtr CSingleDisplayDlg::getConvertedImage()
{
	FrameBufferSPtr sptrConvertedImage;

	while ( _bRunning )
	{
		m_mutexQue.enter();

		if ( _displayQue.empty() )
		{
			m_mutexQue.leave();
			// Sleep(1)是为了防止拉流回调往_displayQue投递速度比显示慢，导致CPU使用率很高 
			//Sleep(1) is to prevent the get frame callback from delivering to the displayque slower than the display, resulting in high CPU utilization
			Sleep(1);

			// 当m_bStopGrabbing为true时，break退出，防止卡死 
			// When m_bstoppgrabbing is true, break exits to prevent jamming
			if(m_bStopGrabbing)
			{
				break;
			}

			continue;
		}
		else
		{
			sptrConvertedImage = _displayQue.front();
			_displayQue.pop_front();

			m_mutexQue.leave();

			break;
		}		
	}

	return sptrConvertedImage;
}

void CSingleDisplayDlg::addConvertedImage(const FrameBufferSPtr& sptrConvertedImage)
{
	static bool bThrowCtrl = true;

	Infra::CGuard guard(m_mutexQue);

	if ( _displayQue.size() > DEFAULT_SHOW_RATE )
	{
		if ( bThrowCtrl )
		{
			_displayQue.pop_front();
			bThrowCtrl = false;
		}
		else
		{
			bThrowCtrl = true;
			return;
		}
	}

	_displayQue.push_back(sptrConvertedImage);
}

RGBQUAD g_rgbQuad[256] = { 0 };
void CSingleDisplayDlg::OnBnClickedBtnSavepic()
{
	// 获取转换后的数据
	// get converted data
	FrameBufferSPtr	pDisplayFrame = getConvertedImage();
	if (!pDisplayFrame )
	{
		TRACE("No data can been saved.\n");
		return;
	}

	static int i = 0;
	int dataSize = 0;
	char appExePath[256] = {0};
	char appDir[256] = {0};
	char saveBmpPath[256] = {0};
	HINSTANCE hInst = AfxGetApp()->m_hInstance;
	GetModuleFileName(hInst, appExePath , sizeof(appExePath));
	_splitpath(appExePath, NULL, appDir, NULL, NULL);
	sprintf(saveBmpPath, "%s\\Image\\Pic_blockId#%d.bmp", appDir, pDisplayFrame->BlockId());

	HANDLE hFile = CreateFile(saveBmpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hFile == INVALID_HANDLE_VALUE)
	{
		return;
	}

	unsigned int uRgbQuadLen = 0;                   //调色板，只有8bit以下才需要 | Palette, only required below 8 bit
	BITMAPFILEHEADER bmpFileHeader;
	BITMAPINFOHEADER bmpInfoHeader;

	// 设置BMP文件头 |set BMP Header
	bmpFileHeader.bfType          = 0x4D42 ;        // 文件头类型 'BM'(42 4D) | type file
	bmpFileHeader.bfSize          = sizeof(bmpFileHeader) + sizeof(bmpInfoHeader) + pDisplayFrame->Width() * pDisplayFrame->Height() * 3 ; //文件大小 | file size
	bmpFileHeader.bfReserved1     = 0 ;             // 保留字 | reserved
	bmpFileHeader.bfReserved2     = 0 ;             // 保留字 | reserved

	// 设置BMP信息头 |set BMP Header
	bmpInfoHeader.biSize = 40;							//信息头所占字节数 | bytes of header
	bmpInfoHeader.biWidth = pDisplayFrame->Width();		//位图宽度  | bitmap width
	bmpInfoHeader.biHeight = pDisplayFrame->Height();	//位图高度  | bitmap high
	bmpInfoHeader.biPlanes = 1;							//位图平面数| number of bitmap planes

	// 转码 
	// transcoding
	if(pDisplayFrame->PixelFormat() == Dahua::GenICam::gvspPixelMono8)
	{
		dataSize = pDisplayFrame->DataSize();
		bmpInfoHeader.biHeight = -pDisplayFrame->Height();
		bmpInfoHeader.biBitCount = 8;
		uRgbQuadLen = sizeof(RGBQUAD)* 256;
		bmpFileHeader.bfSize = sizeof(bmpFileHeader)+sizeof(bmpInfoHeader)+uRgbQuadLen + pDisplayFrame->Width() * pDisplayFrame->Height();
	}
	else
	{
		dataSize = pDisplayFrame->Width() * pDisplayFrame->Height() * 3;
		bmpInfoHeader.biHeight = -pDisplayFrame->Height();	//rgb数据保存为bmp，上下会颠倒，需要设置height为负值 |RGB data is saved as BMP, which will be reversed. You need to set the height to a negative value
		bmpInfoHeader.biBitCount  = 24;						//像素位数 |pixel digit
	}

	bmpFileHeader.bfOffBits = 54 + uRgbQuadLen;     //位图像素数据的起始位置 |starting position of bitmap pixel data

	bmpInfoHeader.biCompression   = 0 ;             //压缩类型，0 即不压缩 |compression type, 0 means no compression
	bmpInfoHeader.biSizeImage     = 0 ;
	bmpInfoHeader.biXPelsPerMeter = 0 ;
	bmpInfoHeader.biYPelsPerMeter = 0 ;
	bmpInfoHeader.biClrUsed       = 0 ;
	bmpInfoHeader.biClrImportant  = 0 ;

	DWORD dwWrite = 0;
	WriteFile(hFile, &bmpFileHeader, sizeof(BITMAPFILEHEADER), &dwWrite, NULL);
	WriteFile(hFile, &(bmpInfoHeader), sizeof(BITMAPINFOHEADER), &dwWrite, NULL);

	if(pDisplayFrame->PixelFormat() == Dahua::GenICam::gvspPixelMono8)
	{
		static bool sbRgbQuadInit = false;
		if (!sbRgbQuadInit)
		{
			for (int i = 0; i < 256; ++i)
			{
				g_rgbQuad[i].rgbBlue = g_rgbQuad[i].rgbGreen = g_rgbQuad[i].rgbRed = i;
			}
			sbRgbQuadInit = true;
		}
		WriteFile(hFile, g_rgbQuad, uRgbQuadLen, &dwWrite, NULL);
	}

	WriteFile(hFile, (BYTE*)(pDisplayFrame->bufPtr()), dataSize, &dwWrite, NULL);
	CloseHandle(hFile);
}
